#pragma once

#include <deque>
#include <string>

#include <ConnectionBroker/Packet.h>
#include <ConnectionBroker/ServerSettings.h>
#include <ConnectionBroker/TcpConnection.h>
#include <ConnectionBroker/TcpMessage.h>
#include <ConnectionBroker/Typedefs.h>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>

namespace ConnectionBroker
{
   class TcpClient : public TcpConnection
   {

   protected: //Member variables
      std::deque<ConnectionBroker::Packet> mWritePackets;
      std::deque<std::pair<std::string, Json::Value> > mWriteChannelMessagePairs;
      std::map<std::string, uint32_t> mChannels;
      messageCallback mOnMessageCallback;
      messageCallback mOnAuthenticationCallback;
      channelsCallback mOnChannelsReceivedCallback;
      bool mConnected;
      bool mChannelsReceived;
      std::string mVersion;
      ServerSettings mSettings;
      
   public:
      TcpClient(boost::asio::io_service& ioService, const ServerSettings& settings, boost::shared_ptr<boost::asio::ssl::context> pSslContext = boost::shared_ptr<boost::asio::ssl::context>());
      virtual ~TcpClient();

   public: //Getters
      bool connected() const { return mConnected; }
      bool channelsReceived() const { return mChannelsReceived; }
      const std::string& version() const { return mVersion; }

   public: //Required overrides
      virtual void messageReceived(const Packet& pPacket);

   public: //Overrides
      virtual void start();

   protected: //Overrides
      virtual void connectionEstablished();
      virtual void onStop();

   public: //Interface
      const std::string& channelName(const uint32_t channelNumber);
      void sendAuthentication(const Json::Value& pAuthInfo);
      void setOnAuthenticationCallback(messageCallback pCallback);
      void setOnChannelsReceivedCallback(channelsCallback pCallback);
      void setOnMessageCallback(messageCallback pCallback);
      void subscribeTo(const std::string& channel);
      void subscribeTo(const std::vector<std::string>& channels);
      void write(const std::string& channelName, const Json::Value& message);
      virtual void write(const Packet& p);

   protected: //Utilities
      void fillChannelMap(const Json::Value& pChannelList);
      void handleChannelListReceived(const Json::Value& pChannelList);
      bool versionCheck(const Json::Value& msgJson);

   protected: //Shared from this
      TcpClientPtr sharedFromThis()
      {
         return boost::static_pointer_cast<TcpClient>(shared_from_this());
      }
   };
}
